home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-01
/
oleo130s.zip
/
OLEO130S.TAR
/
oleo-1.3
/
ref.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-03-31
|
70KB
|
3,005 lines
/* Copyright (C) 1990, 1992, 1993 Free Software Foundation, Inc.
This file is part of Oleo, the GNU Spreadsheet.
Oleo is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
Oleo is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Oleo; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "funcdef.h"
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <signal.h>
#include "sysdef.h"
#include "global.h"
#include "cell.h"
#include "eval.h"
#include "io-abstract.h"
#include "io-generic.h"
#include "hash.h"
#include "byte-compile.h"
#include "parse.h"
#include "ref.h"
#include "cmd.h"
#ifdef __STDC__
static void add_ref_fm (struct ref_fm **, CELLREF, CELLREF);
static void flush_ref_fm (struct ref_fm **, CELLREF, CELLREF);
static void flush_range_ref (struct rng *, CELLREF, CELLREF);
extern void shift_formula (int r, int c, int dn, int ov);
#ifdef SPLIT_REFS
/* These two are tunable paramaters */
#define REF_START 3
#define REF_INC *=2
#else
static void flush_ref_to (struct ref_to **);
static void flush_fm_ref (struct ref_fm *);
#endif /* SPLIT_REFS */
#else /* !__STDC__ */
static void add_ref_fm ();
static void flush_ref_fm ();
static void flush_range_ref ();
extern void shift_formula ();
#ifdef SPLIT_REFS
/* These two are tunable paramaters */
#define REF_START 3
#define REF_INC *=2
#else
static void flush_ref_to ();
static void flush_fm_ref ();
#endif /* SPLIT_REFS */
#endif /* __STDC__ */
/* More tunable paramaters */
#define FIFO_START 40
#define FIFO_INC *=2
#define TO_MAGIC(row,col) (((long)(row)<<BITS_PER_CELLREF)|(col))
#define MAGIC_ROW(magic) (((magic)>>BITS_PER_CELLREF)&CELLREF_MASK)
#define MAGIC_COL(magic) ((magic)&CELLREF_MASK)
#define BETWEEN(mid,lo,hi) ((mid>=lo)&&(mid<=hi))
static VOIDSTAR moving;
int timer_active = 0;
struct ref_fm *timer_cells;
CELL *my_cell;
#ifdef TEST
extern int debug;
#endif
/* Functions for dealing exclusively with variables */
struct hash_control *the_vars;
struct value
{
int type;
union vals c_z;
};
/* For the fifo-buffer */
struct pos
{
CELLREF row;
CELLREF col;
};
struct cell_buf
{
unsigned int size;
struct pos *buf;
struct pos *push_to_here;
struct pos *pop_frm_here;
};
/* Set the cell ROW,COL to STRING, parsing string as needed */
#ifdef __STDC__
void
set_cell (CELLREF row, CELLREF col, char *string)
#else
void
set_cell (row, col, string)
CELLREF row;
CELLREF col;
char *string;
#endif
{
unsigned char *ret;
cur_row = row;
cur_col = col;
#ifdef TEST
if (!string)
{
io_error_msg ("Null string to set_cell %s", cell_name (row, col));
return;
}
#endif
while (*string == ' ')
string++;
if (!*string)
{
my_cell = find_cell (cur_row, cur_col);
if (!my_cell)
return;
flush_old_value ();
return;
}
my_cell = find_or_make_cell (cur_row, cur_col);
flush_old_value ();
ret = parse_and_compile (string);
my_cell->cell_formula = ret;
}
extern int default_lock;
/* new_value() calls set_cell, but refuses to change locked cells, and
updates and prints the results. It returns an error msg on error. . .
*/
#ifdef __STDC__
char *
new_value (CELLREF row, CELLREF col, char *string)
#else
char *
new_value (row, col, string)
CELLREF row;
CELLREF col;
char *string;
#endif
{
CELL *cp;
cp = find_cell (row, col);
if (((!cp || GET_LCK (cp) == LCK_DEF) && default_lock == LCK_LCK) || (cp && GET_LCK (cp) == LCK_LCK))
{
return "cell is locked";
}
set_cell (row, col, string);
if (my_cell)
{
update_cell (my_cell);
if (is_constant (my_cell->cell_formula))
{
byte_free (my_cell->cell_formula);
my_cell->cell_formula = 0;
}
io_pr_cell (row, col, my_cell);
my_cell = 0;
}
return 0;
}
/* This sets the cell to a constant, stored in VALUE, whose type is in TYPE */
#ifdef __STDC__
char *
set_new_value (CELLREF row, CELLREF col, int type, union vals *value)
#else
char *
set_new_value (row, col, type, value)
CELLREF row;
CELLREF col;
int type;
union vals *value;
#endif
{
CELL *cp;
extern int default_lock;
if (type == TYP_ERR)
type = 0;
cur_row = row;
cur_col = col;
if (type == 0)
{
cp = find_cell (row, col);
if (cp && GET_TYP (cp))
{
if ((GET_LCK (cp) == LCK_DEF && default_lock == LCK_LCK) || GET_LCK (cp) == LCK_LCK)
return "cell is locked";
my_cell = cp;
flush_old_value ();
SET_TYP (cp, 0);
}
my_cell = 0;
return 0;
}
else
{
cp = find_or_make_cell (row, col);
if ((GET_LCK (cp) == LCK_DEF && default_lock == LCK_LCK) || GET_LCK (cp) == LCK_LCK)
return "cell is locked";
my_cell = cp;
flush_old_value ();
SET_TYP (cp, type);
/* cp->c_z= *value; */
switch (type)
{
case TYP_FLT:
cp->cell_flt = value->c_d;
cp->cell_formula = 0;
break;
case TYP_INT:
cp->cell_int = value->c_l;
cp->cell_formula = 0;
break;
case TYP_STR:
cp->cell_str = strdup (value->c_s);
cp->cell_formula = 0;
break;
case TYP_BOL:
cp->cell_bol = value->c_i;
cp->cell_formula = 0;
break;
case TYP_ERR:
cp->cell_err = value->c_i;
cp->cell_formula = 0;
break;
#ifdef TEST
default:
panic ("Unknown type %d in set_new_value", GET_TYP (cp));
#endif
}
}
push_refs (cp->cell_refs_from);
io_pr_cell (row, col, cp);
my_cell = 0;
return 0;
}
/* We're reading in a cell, whose formula is FORM, and whose current value
is VAL. Parse both of them. . . (Parsing of VAL is quite primitive)
*/
#ifdef __STDC__
char *
read_new_value (CELLREF row, CELLREF col, char *form, char *val)
#else
char *
read_new_value (row, col, form, val)
CELLREF row;
CELLREF col;
char *form;
char *val;
#endif
{
unsigned char *new_bytes;
extern double __plinf, __neinf, __nan;
cur_row = row;
cur_col = col;
my_cell = find_or_make_cell (cur_row, cur_col);
flush_old_value ();
SET_TYP (my_cell, 0);
if (form)
{
new_bytes = parse_and_compile (form);
my_cell->cell_formula = new_bytes;
}
if (val)
{
if (val[0] == '"')
{
char *sp, *nsp;
sp = val + 1;
SET_TYP (my_cell, TYP_STR);
while (*sp)
sp++;
if (*--sp != '"')
{
if (*sp == '\r' && sp[-1] == '"')
--sp;
else
panic ("Can't find \" in read_new value");
}
*sp = '\0';
nsp = my_cell->cell_str = ck_malloc (sp - val);
for (sp = val + 1; *sp;)
*nsp++ = *sp++;
*nsp++ = '\0';
}
else if (isdigit (val[0]) || val[0] == '.' || val[0] == '-' || val[0] == '+')
{
char *v;
v = val;
SET_TYP (my_cell, TYP_INT);
my_cell->cell_int = astol (&v);
if (*v)
{
SET_TYP (my_cell, TYP_FLT);
v = val;
my_cell->cell_flt = astof (&v);
if (*v)
return "unknown number";
}
}
else if (val[0] == '#')
{
char **en;
if (!stricmp (tname, val))
{
SET_TYP (my_cell, TYP_BOL);
my_cell->cell_bol = 1;
}
else if (!stricmp (fname, val))
{
SET_TYP (my_cell, TYP_BOL);
my_cell->cell_bol = 0;
}
else if (!stricmp (iname, val))
{
SET_TYP (my_cell, TYP_FLT);
my_cell->cell_flt = __plinf;
}
else if (!stricmp (iname, val))
{
SET_TYP (my_cell, TYP_FLT);
my_cell->cell_flt = __plinf;
}
else if (!stricmp (mname, val))
{
SET_TYP (my_cell, TYP_FLT);
my_cell->cell_flt = __neinf;
}
else if (!stricmp (nname, val))
{
SET_TYP (my_cell, TYP_FLT);
my_cell->cell_flt = __nan;
}
else
{
SET_TYP (my_cell, TYP_ERR);
for (en = ename; *en; en++)
if (!stricmp (*en, val))
break;
if (*en)
my_cell->cell_err = en - &ename[0];
else
my_cell->cell_err = 1;
}
}
else
panic ("What is a '%s'?", val);
}
my_cell = 0;
return 0;
}
/* This moves the contents, format, etc from RF,CF to RT,CT RF or RT may be
NON_ROW, in which case the cell's contents are moved to/from a static
storage area. Moving anything from NON_ROW before moving anything into it
or moving two things at a time into NON_ROW are both bad ideas. . .
Also note that move_cell does not call move_outside, which may or may not
be a bug. . . Move_cell is only called as part of sorting, which is why
we may *not* want to call move_outside. . .
*/
#ifdef __STDC__
void
move_cell (CELLREF rf, CELLREF cf, CELLREF rt, CELLREF ct)
#else
void
move_cell (rf, cf, rt, ct)
CELLREF rf;
CELLREF cf;
CELLREF rt;
CELLREF ct;
#endif
{
CELL *cpf;
static CELLREF non_rf, non_cf;
static struct cell non_cell;
if (rf == NON_ROW)
{
cur_row = rt;
cur_col = ct;
my_cell = find_cell (cur_row, cur_col);
if (my_cell)
flush_old_value ();
else if (!non_cell.cell_flags && !non_cell.cell_formula
&& !non_cell.cell_font)
return;
else
my_cell = find_or_make_cell (cur_row, cur_col);
my_cell->cell_flags = non_cell.cell_flags;
my_cell->cell_refs_to = non_cell.cell_refs_to;
my_cell->cell_formula = non_cell.cell_formula;
my_cell->cell_cycle = non_cell.cell_cycle;
my_cell->cell_font = non_cell.cell_font;
my_cell->c_z = non_cell.c_z;
push_refs (my_cell->cell_refs_from);
if (my_cell->cell_refs_to)
shift_formula (cur_row, cur_col, rt - non_rf, ct - non_cf);
my_cell = 0;
return;
}
cpf = find_cell (rf, cf);
if (rt == NON_ROW)
{
non_rf = rf;
non_cf = cf;
if (!cpf)
bzero (&non_cell, sizeof (non_cell));
else
{
non_cell.cell_flags = cpf->cell_flags;
non_cell.cell_refs_to = cpf->cell_refs_to;
non_cell.cell_formula = cpf->cell_formula;
non_cell.cell_cycle = cpf->cell_cycle;
non_cell.cell_font = cpf->cell_font;
non_cell.c_z = cpf->c_z;
cpf->cell_flags = 0;
cpf->cell_refs_to = 0;
cpf->cell_formula = 0;
cpf->cell_cycle = 0;
cpf->cell_font = 0;
}
return;
}
cur_row = rt;
cur_col = ct;
my_cell = find_cell (cur_row, cur_col);
if ((!cpf || (!cpf->cell_flags && !cpf->cell_formula && !cpf->cell_font))
&& !my_cell)
return;
if (!my_cell)
{
my_cell = find_or_make_cell (cur_row, cur_col);
cpf = find_cell (rf, cf); /* FOO */
}
else
flush_old_value ();
if (!cpf)
return;
my_cell->cell_flags = cpf->cell_flags;
my_cell->cell_refs_to = cpf->cell_refs_to;
my_cell->cell_formula = cpf->cell_formula;
my_cell->cell_cycle = cpf->cell_cycle;
my_cell->cell_font = cpf->cell_font;
my_cell->c_z = cpf->c_z;
cpf->cell_flags = 0;
cpf->cell_refs_to = 0;
cpf->cell_formula = 0;
cpf->cell_cycle = 0;
cpf->cell_font = 0;
push_refs (my_cell->cell_refs_from);
if (my_cell->cell_refs_to)
shift_formula (cur_row, cur_col, rt - rf, ct - cf);
my_cell = 0;
}
#ifdef __STDC__
void
copy_cell (CELLREF rf, CELLREF cf, CELLREF rt, CELLREF ct)
#else
void
copy_cell (rf, cf, rt, ct)
CELLREF rf;
CELLREF cf;
CELLREF rt;
CELLREF ct;
#endif
{
CELL *cpf;
cpf = find_cell (rf, cf);
cur_row = rt;
cur_col = ct;
my_cell = find_cell (cur_row, cur_col);
if ((!cpf || (!cpf->cell_flags && !cpf->cell_formula && !cpf->cell_font))
&& !my_cell)
return;
if (!my_cell)
{
my_cell = find_or_make_cell (cur_row, cur_col);
cpf = find_cell (rf, cf); /* FOO */
}
else
flush_old_value ();
if (!cpf)
return;
my_cell->cell_flags = cpf->cell_flags;
my_cell->cell_cycle = cpf->cell_cycle;
my_cell->cell_font = cpf->cell_font;
my_cell->cell_refs_to = cpf->cell_refs_to;
if (my_cell->cell_refs_to)
my_cell->cell_refs_to->refs_refcnt++;
if (GET_TYP (my_cell) == TYP_STR)
my_cell->cell_str = strdup (cpf->cell_str);
else
my_cell->c_z = cpf->c_z;
if (cpf->cell_formula)
{
unsigned char *fp;
unsigned char *hi;
unsigned char byte;
CELLREF trr, tcc;
struct rng trng;
struct function *f;
size_t len;
struct var *v;
CELL *tcp;
fp = cpf->cell_formula;
hi = 0;
if (!moving)
moving = init_stack ();
while ((byte = *fp++) != ENDCOMP)
{
unsigned char * refloc = fp - 1;
if (byte < USR1)
f = &the_funs[byte];
else if (byte < SKIP)
{
int tmp;
#ifdef TEST
if (byte - USR1 >= n_usr_funs)
panic ("Only have %d usr-function slots, but found byte for slot %d", n_usr_funs, 1 + byte - USR1);
#endif
tmp = *fp++;
f = &usr_funs[byte - USR1][tmp];
}
else
f = &skip_funs[byte - SKIP];
if (f->fn_argn & X_J)
fp++;
else if (f->fn_argn & X_JL)
fp += 2;
if ((f->fn_argn & X_ARGS) == X_AN)
fp++;
switch (byte)
{
case CONST_FLT:
fp += sizeof (double);
break;
case CONST_INT:
fp += sizeof (long);
break;
case CONST_STR:
if (!hi)
hi = fp + fp[-1];
break;
case CONST_STR_L:
if (!hi)
hi = fp + fp[-2] + ((unsigned) (fp[-1]) << 8);
break;
case CONST_ERR:
fp += 1 /* +sizeof(char *) */ ;
break;
case VAR:
bcopy (fp, &v, sizeof (struct var *));
fp += sizeof (struct var *);
add_ref_fm (&(v->var_ref_fm), cur_row, cur_col);
switch (v->var_flags)
{
case VAR_UNDEF:
break;
case VAR_CELL:
tcp = find_cell (v->v_rng.lr, v->v_rng.lc);
add_ref_fm (&(tcp->cell_refs_from), cur_row, cur_col);
break;
case VAR_RANGE:
add_range_ref (&(v->v_rng));
/* sparse array bug fixed here */
my_cell = find_cell (cur_row, cur_col);
cpf = find_cell (rf, cf);
break;
}
break;
case R_CELL:
case R_CELL | COLREL:
case R_CELL | ROWREL:
case R_CELL | ROWREL | COLREL:
push_stack (moving, fp);
fp += EXP_ADD;
break;
case RANGE:
case RANGE | LRREL:
case RANGE | LRREL | LCREL:
case RANGE | LRREL | LCREL | HCREL:
case RANGE | LRREL | HCREL:
case RANGE | LRREL | HRREL:
case RANGE | LRREL | HRREL | LCREL:
case RANGE | LRREL | HRREL | LCREL | HCREL:
case RANGE | LRREL | HRREL | HCREL:
case RANGE | HRREL:
case RANGE | HRREL | LCREL:
case RANGE | HRREL | LCREL | HCREL:
case RANGE | HRREL | HCREL:
case RANGE | LCREL:
case RANGE | LCREL | HCREL:
case RANGE | HCREL:
push_stack (moving, fp);
fp += EXP_ADD_RNG;
break;
default:
{
struct function *fun;
if (byte >= SKIP)
break;
if (byte < USR1)
fun = &the_funs[byte];
else
fun = &usr_funs[byte - USR1][refloc[1]];
if (fun->fn_comptype & C_T)
{
add_ref_fm (&timer_cells, rt, ct);
++timer_active;
}
break;
}
}
}
if (!hi)
hi = fp;
else
hi += strlen ((char *) hi);
hi++;
len = hi - cpf->cell_formula;
my_cell->cell_formula = cpf->cell_formula;
cpf->cell_formula = ck_malloc (hi - cpf->cell_formula);
bcopy (my_cell->cell_formula, cpf->cell_formula, len);
while (fp = pop_stack (moving))
{
byte = fp[-1];
if ((byte | ROWREL | COLREL) == (R_CELL | ROWREL | COLREL))
{
trr = GET_ROW (fp);
tcc = GET_COL (fp);
if (byte & ROWREL)
{
trr += rt - rf;
PUT_ROW (fp, trr);
}
if (byte & COLREL)
{
tcc += ct - cf;
PUT_COL (fp, tcc);
}
tcp = find_or_make_cell (trr, tcc);
add_ref_fm (&(tcp->cell_refs_from), cur_row, cur_col);
}
#ifdef TEST
else if ((byte | LRREL | HRREL | LCREL | HCREL) !=
(RANGE | LRREL | HRREL | LCREL | HCREL))
panic ("Unknown byte %x in copy_cell", byte);
#endif
else
{
GET_RNG (fp, &trng);
if (byte & LRREL)
trng.lr += rt - rf;
if (byte & HRREL)
trng.hr += rt - rf;
if (byte & LCREL)
trng.lc += ct - cf;
if (byte & HCREL)
trng.hc += ct - cf;
PUT_RNG (fp, &trng);
add_range_ref (&trng);
/* sparse array bug fixed here */
my_cell = find_cell (cur_row, cur_col);
cpf = find_cell (rf, cf);
}
}
update_cell (my_cell);
}
else
{
my_cell->cell_formula = 0;
}
io_pr_cell (cur_row, cur_col, my_cell);
push_refs (my_cell->cell_refs_from);
my_cell = 0;
}
/* Take away the value of CP. This means getting rid of all the references
to it, etc.
*/
#ifdef __STDC__
void
flush_old_value (void)
#else
void
flush_old_value ()
#endif
{
struct ref_to *ref;
unsigned char *refloc;
int n;
unsigned char byte;
CELL *other_cell;
struct var *varp;
ref = my_cell->cell_refs_to;
if (ref)
{
for (n = 0; n < ref->refs_used; n++)
{
/* Switch on formula[ref->to_refs[n]] */
refloc = &(my_cell->cell_formula[ref->to_refs[n]]);
byte = refloc[0];
switch (byte)
{
case F_ROW:
case F_COL:
break;
case R_CELL:
case R_CELL | ROWREL:
case R_CELL | COLREL:
case R_CELL | ROWREL | COLREL:
other_cell = find_cell (GET_ROW (refloc + 1), GET_COL (refloc + 1));
if (other_cell)
flush_ref_fm (&(other_cell->cell_refs_from), cur_row, cur_col);
#ifdef TEST
else
io_error_msg ("Can't find other_cell in flush_old_value");
#endif
break;
case RANGE:
case RANGE | LRREL:
case RANGE | LRREL | LCREL:
case RANGE | LRREL | LCREL | HCREL:
case RANGE | LRREL | HCREL:
case RANGE | LRREL | HRREL:
case RANGE | LRREL | HRREL | LCREL:
case RANGE | LRREL | HRREL | LCREL | HCREL:
case RANGE | LRREL | HRREL | HCREL:
case RANGE | HRREL:
case RANGE | HRREL | LCREL:
case RANGE | HRREL | LCREL | HCREL:
case RANGE | HRREL | HCREL:
case RANGE | LCREL:
case RANGE | LCREL | HCREL:
case RANGE | HCREL:
{
struct rng rng;
GET_RNG (refloc + 1, &rng);
flush_range_ref (&rng, cur_row, cur_col);
}
break;
case VAR:
bcopy (&refloc[1], &varp, sizeof (struct var *));
flush_ref_fm (&(varp->var_ref_fm), cur_row, cur_col);
if (varp->var_flags == VAR_CELL)
{
other_cell = find_cell (varp->v_rng.lr, varp->v_rng.lc);
if (other_cell)
flush_ref_fm (&(other_cell->cell_refs_from), cur_row, cur_col);
}
else if (varp->var_flags == VAR_RANGE)
flush_range_ref (&(varp->v_rng), cur_row, cur_col);
#ifdef TEST
else if (varp->var_flags != VAR_UNDEF)
panic ("Unknown var type %d", varp->var_flags);
#endif
break;
default:
{
struct function *fun;
if (byte < USR1)
fun = &the_funs[byte];
#ifdef TEST
else if (byte >= SKIP)
fun = 0, panic ("SKIP? in flush_old_value()");
#endif
else
fun = &usr_funs[byte - USR1][refloc[1]];
if (fun->fn_comptype & C_T)
{
#ifdef TEST
if (!timer_cells || !timer_cells->refs_used)
panic ("No timer cells in flush_timer_cell");
#endif
flush_ref_fm (&timer_cells, cur_row, cur_col);
--timer_active;
break;
}
else
io_error_msg ("Bad ref_to of %d.%x ignored", ref->to_refs[n], byte);
}
break;
}
}
#ifdef SPLIT_REFS
ref->refs_used = 0;
#else
flush_ref_to (&(my_cell->cell_refs_to));
#endif
}
if (my_cell->cell_formula)
{
byte_free (my_cell->cell_formula);
my_cell->cell_formula = 0;
}
if (GET_TYP (my_cell) == TYP_STR)
free (my_cell->cell_str);
SET_TYP (my_cell, 0);
}
/* --------- Routines for dealing with cell references to other cells ------ */
#ifdef __STDC__
void
add_ref (CELLREF row, CELLREF col)
#else
void
add_ref (row, col)
CELLREF row;
CELLREF col;
#endif
{
CELL *other_cell;
other_cell = find_or_make_cell (row, col);
add_ref_fm (&(other_cell->cell_refs_from), cur_row, cur_col);
}
#ifdef __STDC__
void
add_range_ref (struct rng *rng)
#else
void
add_range_ref (rng)
struct rng *rng;
#endif
{
CELL *other_cell;
struct ref_fm *oldref, *newref;
struct ref_fm nonref;
make_cells_in_range (rng);
/* Be efficient: If cells in the range currently have the same
* references, they'll have the same references afterward, so just
* adjust the refcounts
*/
nonref.refs_refcnt = 1;
other_cell = next_cell_in_range ();
oldref = other_cell->cell_refs_from;
if (oldref && oldref->refs_refcnt == 1)
oldref = &nonref;
add_ref_fm (&(other_cell->cell_refs_from), cur_row, cur_col);
newref = other_cell->cell_refs_from;
while (other_cell = next_cell_in_range ())
{
if (other_cell->cell_refs_from == oldref)
{
if (oldref)
{
if (oldref->refs_refcnt == 1)
{
flush_fm_ref (oldref);
oldref = &nonref;
}
else
oldref->refs_refcnt--;
}
other_cell->cell_refs_from = newref;
newref->refs_refcnt++;
}
else if (oldref == &nonref && (!other_cell->cell_refs_from || other_cell->cell_refs_from->refs_refcnt > 1))
{
oldref = other_cell->cell_refs_from;
add_ref_fm (&(other_cell->cell_refs_from), cur_row, cur_col);
newref = other_cell->cell_refs_from;
}
else
add_ref_fm (&(other_cell->cell_refs_from), cur_row, cur_col);
}
/* if(oldref && oldref->refs_refcnt==0) {
oldref->refs_refcnt=1;
flush_fm_ref(oldref);
} */
}
#ifdef __STDC__
static void
flush_range_ref (struct rng *rng, CELLREF rr, CELLREF cc)
#else
static void
flush_range_ref (rng, rr, cc)
struct rng *rng;
CELLREF rr;
CELLREF cc;
#endif
{
CELL *other_cell;
#ifndef SPLIT_REFS
struct ref_fm *oldref, *newref;
struct ref_fm nonref;
#endif
/* This is horribly inefficient: Simply referencing a cell makes
it appear. On the other hand, there is no other easy way to deal
with the references to the cells (That I know of, anyway) */
find_cells_in_range (rng);
#ifndef SPLIT_REFS
/* Be efficient: If cells in the range currently have the same
references, they'll have the same references afterward, so just
adjust the refcounts */
nonref.refs_refcnt = 1;
other_cell = next_cell_in_range ();
if (!other_cell)
return;
oldref = other_cell->cell_refs_from;
if (oldref && oldref->refs_refcnt == 1)
oldref = &nonref;
flush_ref_fm (&(other_cell->cell_refs_from), rr, cc);
newref = other_cell->cell_refs_from;
while (other_cell = next_cell_in_range ())
{
if (other_cell->cell_refs_from == oldref)
{
if (oldref)
{
if (oldref->refs_refcnt == 1)
{
flush_fm_ref (oldref);
oldref = &nonref;
}
else
oldref->refs_refcnt--;
}
other_cell->cell_refs_from = newref;
if (newref)
newref->refs_refcnt++;
}
else if (oldref == &nonref && (!other_cell->cell_refs_from || other_cell->cell_refs_from->refs_refcnt > 1))
{
oldref = other_cell->cell_refs_from;
flush_ref_fm (&(other_cell->cell_refs_from), rr, cc);
newref = other_cell->cell_refs_from;
}
else
flush_ref_fm (&(other_cell->cell_refs_from), rr, cc);
}
#else
while (other_cell = next_cell_in_range ())
flush_ref_fm (&(other_cell->cell_refs_from), rr, cc);
#endif
}
/* SPLIT_REFS should probably go away, since it is a performance loss.
When it is defined, we allocate a a reference structure for each cell with
references, and update them individually.
The default is to allocate one reference structure for each *different*
reference, and have multiple cells point to it. We use reference counts
to keep track of when to delete the reference structure. This would
appear to be a performance loss, but because of the increased efficiency
in referencing ranges, it may actually be faster. It also uses *far* less
memory than SPLIT_REFS */
#ifndef SPLIT_REFS
#ifdef __TURBOC__
#define FM_HASH_NUM 51
#define TO_HASH_NUM 13
#else
#define FM_HASH_NUM 503
#define TO_HASH_NUM 29
#endif
#ifdef TEST
static int fm_misses = 0;
static int to_misses = 0;
#endif
static struct ref_fm *fm_list[FM_HASH_NUM];
static struct ref_fm *fm_tmp_ref;
static unsigned fm_tmp_ref_alloc;
static struct ref_to *to_list[TO_HASH_NUM];
static struct ref_to *to_tmp_ref;
static unsigned to_tmp_ref_alloc;
#ifdef __STDC__
void
flush_refs (void)
#else
void
flush_refs ()
#endif
{
int n;
struct ref_fm *ftmp, *oftmp;
struct ref_to *ttmp, *ottmp;
for (n = 0; n < FM_HASH_NUM; n++)
{
for (ftmp = fm_list[n]; ftmp; ftmp = oftmp)
{
oftmp = ftmp->refs_next;
free (ftmp);
}
fm_list[n] = 0;
}
for (n = 0; n < TO_HASH_NUM; n++)
{
for (ttmp = to_list[n]; ttmp; ttmp = ottmp)
{
ottmp = ttmp->refs_next;
free (ttmp);
}
to_list[n] = 0;
}
}
#ifdef __STDC__
static struct ref_fm *
find_fm_ref (void)
#else
static struct ref_fm *
find_fm_ref ()
#endif
{
struct ref_fm *tmp;
int n;
unsigned long hash;
#if 1
for (hash = 0, n = 0; n < fm_tmp_ref->refs_used; n++)
{
hash += (n + 1) * (((fm_tmp_ref->fm_refs[n].ref_row) << BITS_PER_CELLREF) +
fm_tmp_ref->fm_refs[n].ref_col);
}
hash %= FM_HASH_NUM;
#else
hash = fm_tmp_ref->refs_used;
#endif
for (tmp = fm_list[hash]; tmp; tmp = tmp->refs_next)
{
if (tmp->refs_used != fm_tmp_ref->refs_used)
continue;
if (!bcmp (tmp->fm_refs, fm_tmp_ref->fm_refs, fm_tmp_ref->refs_used * sizeof (struct ref_array)))
{
tmp->refs_refcnt++;
return tmp;
}
#ifdef TEST
else
fm_misses++;
#endif
}
tmp = ck_malloc (sizeof (struct ref_fm) + (fm_tmp_ref->refs_used - 1) * sizeof (struct ref_array));
tmp->refs_next = fm_list[hash];
fm_list[hash] = tmp;
tmp->refs_refcnt = 1;
tmp->refs_used = fm_tmp_ref->refs_used;
bcopy (fm_tmp_ref->fm_refs, tmp->fm_refs, tmp->refs_used * sizeof (struct ref_array));
return tmp;
}
#ifdef __STDC__
static void
flush_fm_ref (struct ref_fm *old)
#else
static void
flush_fm_ref (old)
struct ref_fm *old;
#endif
{
struct ref_fm *tmp;
int n;
unsigned long hash;
--(old->refs_refcnt);
#ifdef DEFER_FREE
return;
#endif
if (!old->refs_refcnt)
{
#if 1
for (hash = 0, n = 0; n < old->refs_used; n++)
{
hash += (n + 1) * (((old->fm_refs[n].ref_row) << BITS_PER_CELLREF) +
old->fm_refs[n].ref_col);
}
hash %= FM_HASH_NUM;
#else
hash = old->refs_used;
#endif
if (fm_list[hash] == old)
fm_list[hash] = old->refs_next;
else
{
for (tmp = fm_list[hash]; tmp && tmp->refs_next != old; tmp = tmp->refs_next)
;
#ifdef TEST
if (!tmp)
{
io_error_msg ("Old not in refs_list in flush_fm_ref(%p)", old);
return;
}
#endif
tmp->refs_next = old->refs_next;
}
free (old);
}
}
/* This adds a from reference to a cells reference list.
* Note that the ref_fm structures themselves are hash-consed.
*/
#ifdef __STDC__
static void
add_ref_fm (struct ref_fm **where, CELLREF r, CELLREF c)
#else
static void
add_ref_fm (where, r, c)
struct ref_fm **where;
CELLREF r;
CELLREF c;
#endif
{
struct ref_fm *from;
int n;
from = *where;
if (!from)
{
if (!fm_tmp_ref)
{
fm_tmp_ref = ck_malloc (sizeof (struct ref_fm));
fm_tmp_ref_alloc = 1;
}
fm_tmp_ref->refs_used = 1;
fm_tmp_ref->fm_refs[0].ref_row = r;
fm_tmp_ref->fm_refs[0].ref_col = c;
}
else
{
if (fm_tmp_ref_alloc <= from->refs_used)
{
fm_tmp_ref =
ck_realloc (fm_tmp_ref, sizeof (struct ref_fm)
+ from->refs_used * sizeof (struct ref_array)) ;
fm_tmp_ref_alloc = from->refs_used + 1;
}
fm_tmp_ref->refs_used = from->refs_used + 1;
n = 0;
while (n < from->refs_used
&& (from->fm_refs[n].ref_row < r
|| (from->fm_refs[n].ref_row == r && from->fm_refs[n].ref_col <= c)))
{
fm_tmp_ref->fm_refs[n] = from->fm_refs[n];
n++;
}
fm_tmp_ref->fm_refs[n].ref_row = r;
fm_tmp_ref->fm_refs[n].ref_col = c;
while (n < from->refs_used)
{
fm_tmp_ref->fm_refs[n + 1] = from->fm_refs[n];
n++;
}
}
*where = find_fm_ref ();
if (from)
flush_fm_ref (from);
}
#ifdef __STDC__
static void
flush_ref_fm (struct ref_fm **where, CELLREF r, CELLREF c)
#else
static void
flush_ref_fm (where, r, c)
struct ref_fm **where;
CELLREF r;
CELLREF c;
#endif
{
struct ref_fm *from;
int n;
from = *where;
#ifdef TEST
if (!from)
{
io_error_msg ("No refs in flush_ref_fm(%p,%u,%u)", where, r, c);
return;
}
#endif
if (!from)
return;
if (from->refs_used == 1)
{
*where = 0;
flush_fm_ref (from);
return;
}
fm_tmp_ref->refs_used = from->refs_used - 1;
n = 0;
while (n < from->refs_used
&& (from->fm_refs[n].ref_row < r
|| (from->fm_refs[n].ref_row == r && from->fm_refs[n].ref_col < c)))
{
fm_tmp_ref->fm_refs[n] = from->fm_refs[n];
n++;
}
#ifdef TEST
if (n == from->refs_used)
{
io_error_msg ("No refs from %u,%u in %p in flush_refs_fm", r, c, where);
return;
}
#endif
while (n < fm_tmp_ref->refs_used)
{
fm_tmp_ref->fm_refs[n] = from->fm_refs[n + 1];
n++;
}
*where = find_fm_ref ();
flush_fm_ref (from);
}
#ifdef TEST
void
dbg_print_ref_fm (rf)
struct ref_fm *rf;
{
int nr;
char *bufp;
if (rf)
{
io_text_line ("fm %p: refcnt %u next %p used %u",
rf, rf->refs_refcnt, rf->refs_next, rf->refs_used);
for (nr = 0, bufp = print_buf; nr < rf->refs_used; nr++)
{
(void) sprintf (bufp, " %s", cell_name (rf->fm_refs[nr].ref_row, rf->fm_refs[nr].ref_col));
if (nr % 10 == 9)
{
io_text_line (print_buf);
bufp = print_buf;
}
else
bufp += strlen (bufp);
}
if (nr % 10)
io_text_line (print_buf);
}
}
#endif
#ifdef __STDC__
static struct ref_to *
find_to_ref (void)
#else
static struct ref_to *
find_to_ref ()
#endif
{
struct ref_to *tmp;
int n;
unsigned long hash;
/* io_error_msg("find_to_ref %u %u",to_tmp_ref->refs_used,to_tmp_ref->to_refs[0]); */
#if 1
for (hash = 0, n = 0; n < to_tmp_ref->refs_used; n++)
hash += (n + 1) * to_tmp_ref->to_refs[n];
hash %= TO_HASH_NUM;
#else
hash = to_tmp_ref->refs_used;
#endif
for (tmp = to_list[hash]; tmp; tmp = tmp->refs_next)
{
/* io_error_msg("%p(%u)->%p %u %u",tmp,tmp->refs_refcnt,
tmp->refs_next,tmp->refs_used,tmp->to_refs[0]); */
if (tmp->refs_used != to_tmp_ref->refs_used)
continue;
if (!bcmp (tmp->to_refs, to_tmp_ref->to_refs, to_tmp_ref->refs_used))
{
/* io_error_msg("Hit!"); */
tmp->refs_refcnt++;
return tmp;
}
#ifdef TEST
else
to_misses++;
#endif
}
/* io_error_msg("Miss. .."); */
tmp = ck_malloc (sizeof (struct ref_to) + to_tmp_ref->refs_used - 1);
tmp->refs_next = to_list[hash];
to_list[hash] = tmp;
tmp->refs_refcnt = 1;
tmp->refs_used = to_tmp_ref->refs_used;
bcopy (to_tmp_ref->to_refs, tmp->to_refs, tmp->refs_used);
return tmp;
}
#ifdef __STDC__
void
add_ref_to (int whereto)
#else
void
add_ref_to (whereto)
int whereto;
#endif
{
struct ref_to *from;
int n;
from = my_cell->cell_refs_to;
if (!from)
{
if (!to_tmp_ref)
{
to_tmp_ref = ck_malloc (sizeof (struct ref_to));
to_tmp_ref_alloc = 1;
}
to_tmp_ref->refs_used = 1;
to_tmp_ref->to_refs[0] = whereto;
}
else
{
if (to_tmp_ref_alloc <= from->refs_used)
{
to_tmp_ref = ck_realloc (to_tmp_ref, sizeof (struct ref_to) + from->refs_used);
to_tmp_ref_alloc = from->refs_used + 1;
}
to_tmp_ref->refs_used = from->refs_used + 1;
n = 0;
while (n < from->refs_used && from->to_refs[n] < whereto)
{
to_tmp_ref->to_refs[n] = from->to_refs[n];
n++;
}
to_tmp_ref->to_refs[n] = whereto;
while (n < from->refs_used)
{
to_tmp_ref->to_refs[n + 1] = from->to_refs[n];
n++;
}
flush_ref_to (&(my_cell->cell_refs_to));
}
my_cell->cell_refs_to = find_to_ref ();
}
#ifdef __STDC__
static void
flush_ref_to (struct ref_to **where)
#else
static void
flush_ref_to (where)
struct ref_to **where;
#endif
{
struct ref_to *tmp;
struct ref_to *old;
int n;
unsigned long hash;
#ifdef TEST
if (!where || !*where)
{
io_error_msg ("null flush_ref_to(%p)", where);
return;
}
#endif
old = *where;
*where = 0;
--(old->refs_refcnt);
#ifdef DEFER_FREE
return;
#endif
if (!old->refs_refcnt)
{
#if 1
for (hash = 0, n = 0; n < old->refs_used; n++)
hash += (n + 1) * old->to_refs[n];
hash %= TO_HASH_NUM;
#else
hash = old->refs_used;
#endif
if (to_list[hash] == old)
to_list[hash] = old->refs_next;
else
{
for (tmp = to_list[hash]; tmp && tmp->refs_next != old; tmp = tmp->refs_next)
;
#ifdef TEST
if (!tmp)
{
io_error_msg ("Old not in refs_list in flush_to_ref(%p)", old);
return;
}
#endif
tmp->refs_next = old->refs_next;
}
free (old);
}
}
#ifdef TEST
void
dbg_print_ref_to (rt, form)
struct ref_to *rt;
unsigned char *form;
{
int nr;
char *bufp;
if (rt)
{
io_text_line ("to %p: refcnt %u next %p used %u",
rt, rt->refs_refcnt, rt->refs_next, rt->refs_used);
for (nr = 0, bufp = print_buf; nr < rt->refs_used; nr++)
{
(void) sprintf (bufp, " %3d (%#4x)", rt->to_refs[nr], form[rt->to_refs[nr]]);
if (nr % 7 == 6)
{
io_text_line (print_buf);
bufp = print_buf;
}
else
bufp += strlen (bufp);
}
if (nr % 7)
io_text_line (print_buf);
}
}
void
ref_stats ()
{
int n;
int cur;
struct ref_fm *rf;
struct ref_to *rt;
int rf_max = 0;
int rf_num = 0;
int rf_shared = 0;
int rf_saved = 0;
int rf_zero = 0;
int rt_max = 0;
int rt_num = 0;
int rt_shared = 0;
int rt_saved = 0;
int rt_zero = 0;
for (n = 0; n < FM_HASH_NUM; n++)
{
cur = 0;
for (rf = fm_list[n]; rf; rf = rf->refs_next)
{
if (rf->refs_refcnt == 0)
rf_zero++;
if (rf->refs_refcnt > 1)
{
rf_shared++;
rf_saved += rf->refs_refcnt - 1;
}
rf_num++;
cur++;
}
if (cur > rf_max)
rf_max = cur;
}
for (n = 0; n < TO_HASH_NUM; n++)
{
cur = 0;
for (rt = to_list[n]; rt; rt = rt->refs_next)
{
if (rt->refs_refcnt == 0)
rt_zero++;
if (rt->refs_refcnt > 1)
{
rt_shared++;
rt_saved += rt->refs_refcnt - 1;
}
rt_num++;
cur++;
}
if (cur > rt_max)
rt_max = cur;
}
io_text_line ("from: %d refs, max_length %d, shared %d, saved %d, zero_ref %d, missed %d\n", rf_num, rf_max, rf_shared, rf_saved, rf_zero, fm_misses);
io_text_line ("to: %d refs, max_length %d, shared %d, saved %d, zero_ref %d, missed %d\n", rt_num, rt_max, rt_shared, rt_saved, rt_zero, to_misses);
}
#endif
#else
#ifdef __STDC__
static void
add_ref_fm (struct ref_fm **where, CELLREF r, CELLREF c)
#else
static void
add_ref_fm (where, r, c)
struct ref_fm **where;
CELLREF r;
CELLREF c;
#endif
{
struct ref_fm *ref;
ref = *where;
if (!ref)
{
*where = ref = ck_malloc (sizeof (struct ref_fm) + (REF_START - 1) * sizeof (struct ref_array));
ref->refs_alloc = REF_START;
ref->refs_used = 0;
}
else if (ref->refs_alloc == ref->refs_used)
{
ref->refs_alloc REF_INC;
*where = ref = ck_realloc (ref, sizeof (struct ref_fm) + (ref->refs_alloc - 1) * sizeof (struct ref_array));
}
ref->fm_refs[ref->refs_used].ref_row = r;
ref->fm_refs[ref->refs_used].ref_col = c;
ref->refs_used++;
}
#ifdef __STDC__
static void
flush_ref_fm (struct ref_fm **where, CELLREF r, CELLREF c)
#else
static void
flush_ref_fm (where, r, c)
struct ref_fm **where;
CELLREF r;
CELLREF c;
#endif
{
int n;
struct ref_fm *ref;
ref = *where;
#ifdef TEST
if (!ref)
{
io_error_msg ("%s->No refs in flush_ref_fm(%d,%d)", cell_name (cur_row, cur_col), r, c);
return;
}
#endif
for (n = 0; n < ref->refs_used; n++)
if (ref->fm_refs[n].ref_row == r && ref->fm_refs[n].ref_col == c)
{
ref->fm_refs[n] = ref->fm_refs[ref->refs_used - 1];
--(ref->refs_used);
return;
}
#ifdef TEST
io_error_msg ("%s->Can't flush_ref_fm(%d,%d)", cell_name (cur_row, cur_col), r, c);
return;
#endif
}
#ifdef __STDC__
void
add_ref_to (int whereto)
#else
void
add_ref_to (whereto)
int whereto;
#endif
{
struct ref_to *ref;
ref = my_cell->cell_refs_to;
if (!ref)
{
my_cell->cell_refs_to = ref = ck_malloc (sizeof (struct ref_to) + (REF_START - 1) * sizeof (unsigned char));
ref->refs_alloc = REF_START;
ref->refs_used = 0;
}
else if (ref->refs_alloc == ref->refs_used)
{
ref->refs_alloc REF_INC;
my_cell->cell_refs_to = ref = ck_realloc (ref, sizeof (struct ref_to) +
(ref->refs_alloc - 1) * sizeof (unsigned char));
}
ref->to_refs[ref->refs_used] = whereto;
ref->refs_used++;
}
#ifdef TEST
void
dbg_print_ref_fm (rf)
struct ref_fm *rf;
{
int nr;
char *bufp;
if (rf)
{
io_text_line ("fm %p: alloc %u used %u",
rf, rf->refs_alloc, rf->refs_used);
for (nr = 0, bufp = print_buf; nr < rf->refs_used; nr++)
{
(void) sprintf (bufp, " %s", cell_name (rf->fm_refs[nr].ref_row, rf->fm_refs[nr].ref_col));
if (nr % 10 == 9)
{
io_text_line (print_buf);
bufp = print_buf;
}
else
bufp += strlen (bufp);
}
if (nr % 10)
io_text_line (print_buf);
}
}
void
dbg_print_ref_to (rt, form)
struct ref_to *rt;
unsigned char *form;
{
int nr;
char *bufp;
if (rt)
{
io_text_line ("to %p: alloc %u used %u",
rt, rt->refs_alloc, rt->refs_used);
for (nr = 0, bufp = print_buf; nr < rt->refs_used; nr++)
{
(void) sprintf (bufp, " %3d (%#4x)", rt->to_refs[nr], form[rt->to_refs[nr]]);
if (nr % 7 == 6)
{
io_text_line (print_buf);
bufp = print_buf;
}
else
bufp += strlen (bufp);
}
if (nr % 7)
io_text_line (print_buf);
}
}
void
ref_stats ()
{
CELL *cp;
int rf_num = 0;
int rf_zero = 0;
int rf_biggest = 0;
int rt_num = 0;
int rt_zero = 0;
int rt_biggest = 0;
find_cells_in_range (&all_rng);
while (cp = next_cell_in_range ())
{
if (cp->cell_refs_from)
{
if (cp->cell_refs_from->refs_used > rf_biggest)
rf_biggest = cp->cell_refs_from->refs_used;
if (cp->cell_refs_from->refs_used == 0)
rf_zero++;
rf_num++;
}
if (cp->cell_refs_to)
{
if (cp->cell_refs_to->refs_used > rt_biggest)
rt_biggest = cp->cell_refs_to->refs_used;
if (cp->cell_refs_to->refs_used == 0)
rt_zero++;
rt_num++;
}
}
io_text_line ("from: %d refs, biggest %d, zero_ref %d\n", rf_num, rf_biggest, rf_zero);
io_text_line ("to: %d refs, biggest %d,zero_ref %d\n", rt_num, rt_biggest, rt_zero);
}
#endif
#endif
/* ------------- Routines for dealing with moving cells -------------------- */
static struct rng *shift_fm;
static int shift_ov;
static int shift_dn;
/* This removes all the CELL_REF_FM links associated with a
* variable, and adjusts the variables value.
* After calling this function, one must also call
* finish_shift_var to install the new CELL_REF_FM links.
*/
#ifdef __STDC__
static void
start_shift_var (char *name, struct var *v)
#else
static void
start_shift_var (name, v)
char *name;
struct var *v;
#endif
{
int n;
int nn;
n = (BETWEEN (v->v_rng.hc, shift_fm->lc, shift_fm->hc) << 3)
+ (BETWEEN (v->v_rng.lc, shift_fm->lc, shift_fm->hc) << 2)
+ (BETWEEN (v->v_rng.hr, shift_fm->lr, shift_fm->hr) << 1)
+ BETWEEN (v->v_rng.lr, shift_fm->lr, shift_fm->hr);
switch (n)
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 8:
case 12:
/* Null intersection, ignore it */
break;
case 5: /* The bottom and right */
case 6: /* The bottom and left */
case 9: /* The top and right */
case 10: /* The top and left */
/* The var sticks out of the range we're moving */
/* on two sides. what should we do? */
io_error_msg ("'%s' can't be adjusted", v->var_name);
break;
case 7: /* v->hc sticks out the right */
case 11: /* v->lc sticks out the left */
case 13: /* v->hr sticks out the bottom */
case 14: /* v->lr sticks out the top */
/* It only sticks out on one side. We can
(try to) adjust it */
case 15: /* var is completely inside the range */
if (v->var_ref_fm)
{
for (nn = 0; nn < v->var_ref_fm->refs_used; nn++)
{
flush_range_ref (&(v->v_rng),
v->var_ref_fm->fm_refs[nn].ref_row,
v->var_ref_fm->fm_refs[nn].ref_col);
}
}
if (n != 7)
v->v_rng.hc += shift_ov;
if (n != 11)
v->v_rng.lc += shift_ov;
if (n != 13)
v->v_rng.hr += shift_dn;
if (n != 14)
v->v_rng.lr += shift_dn;
v->var_flags = VAR_DANGLING_RANGE;
}
}
#ifdef __STDC__
static void
finish_shift_var (char *name, struct var *v)
#else
static void
finish_shift_var (name, v)
char *name;
struct var *v;
#endif
{
int n;
if (!v->var_ref_fm || v->var_flags != VAR_DANGLING_RANGE)
return;
v->var_flags = VAR_RANGE;
for (n = 0; n < v->var_ref_fm->refs_used; n++)
{
cur_row = v->var_ref_fm->fm_refs[n].ref_row;
cur_col = v->var_ref_fm->fm_refs[n].ref_col;
add_range_ref (&(v->v_rng));
}
}
#define RIGHT 8
#define LEFT 4
#define BOTTOM 2
#define TOP 1
/*
* This iterates over the region FM, preparing the cells there to be shifted
* OV(er) and D(ow)N.
*
* After this, the ref_fm and ref_to lists of a cell within the region should
* be appropriate to the location that cell will be shifted to.
*
* Variables and references to variables are also shifted.
*/
#ifdef __STDC__
void
shift_outside (struct rng *fm, int dn, int ov)
#else
void
shift_outside (fm, dn, ov)
struct rng *fm;
int dn;
int ov;
#endif
{
CELL *cp;
CELL *fcp;
CELL *tcp;
int n;
int fn;
CELLREF rr, cc;
CELLREF frr, fcc;
CELLREF trr, tcc;
unsigned char *ffp;
unsigned char *fp;
struct rng orng;
char *ptr;
unsigned long val;
static char DEF_REF[] = "DEFREF";
static char DEF_RNG[] = "DEFRNG";
/* Variables and references to variables are also shifted. */
shift_fm = fm;
shift_dn = dn;
shift_ov = ov;
for_all_vars (start_shift_var);
/* This stack is used to defer adjustments to references that are entirely
* within FM. Intra-FM references are adjusted after references into and
* out of FM.
*/
if (!moving)
moving = init_stack ();
find_cells_in_range (fm);
while (cp = next_row_col_in_range (&rr, &cc))
{
/* cp/rr/cc is a cell in FM. */
/* First, adjust references FROM the region */
if (cp->cell_refs_to)
{
for (n = 0; n < cp->cell_refs_to->refs_used; n++)
{
fp = &(cp->cell_formula[cp->cell_refs_to->to_refs[n]]);
switch (*fp)
{
case R_CELL:
case R_CELL | ROWREL:
case R_CELL | COLREL:
case R_CELL | ROWREL | COLREL:
/* Trr/cc/cp is the cell being referenced */
trr = GET_ROW (fp + 1);
tcc = GET_COL (fp + 1);
tcp = find_cell (trr, tcc);
/* Get rid of the backpointer to this reference. */
flush_ref_fm (&(tcp->cell_refs_from), rr, cc);
/* frr/fcc is the new address of the referenced cell.
* The address will change if Trr/cc happens to be
* in the region that is moving, or if the reference
* is a relative reference.
*/
fn = (BETWEEN (trr, fm->lr, fm->hr)
&& BETWEEN (tcc, fm->lc, fm->hc));
frr = (fn || (((*fp) & ROWREL))) ? trr + dn : trr;
fcc = (fn || (((*fp) & COLREL))) ? tcc + ov : tcc;
PUT_ROW (fp + 1, frr); /* Adjust the formula byte-code. */
PUT_COL (fp + 1, fcc); /* This might even be a noop. */
/* Reinstall the backreference, unless the new address of the
* referenced cell is w/in the region being moved. (In which
* case, defer making the backreference).
*/
if (BETWEEN(frr, fm->lr, fm->hr) && BETWEEN(fcc, fm->lc, fm->hc))
{
push_stack (moving, (VOIDSTAR) TO_MAGIC (rr + dn, cc + ov));
push_stack (moving, (VOIDSTAR) TO_MAGIC (frr, fcc));
push_stack (moving, DEF_REF);
}
else
{
tcp = find_or_make_cell (frr, fcc);
add_ref_fm (&(tcp->cell_refs_from), rr + dn, cc + ov);
cp = find_cell (rr, cc);
}
break;
case VAR:
{
struct var * varp;
bcopy ((VOIDSTAR) (fp + 1),
(VOIDSTAR)&varp, sizeof (struct var *));
flush_ref_fm (&varp->var_ref_fm, rr, cc);
add_ref_fm (&varp->var_ref_fm, rr + dn, cc + ov);
break;
}
case RANGE:
case RANGE | LRREL:
case RANGE | HRREL:
case RANGE | LCREL:
case RANGE | HCREL:
case RANGE | LRREL | HRREL:
case RANGE | LRREL | LCREL:
case RANGE | LRREL | HCREL:
case RANGE | HRREL | LCREL:
case RANGE | HRREL | HCREL:
case RANGE | LCREL | HCREL:
case RANGE | LRREL | LCREL | HCREL:
case RANGE | LRREL | HRREL | LCREL:
case RANGE | LRREL | HRREL | HCREL:
case RANGE | HRREL | LCREL | HCREL:
case RANGE | LRREL | HRREL | LCREL | HCREL:
/* orng is the range being referenced. */
GET_RNG (fp + 1, &orng);
/* Get rid of backpointers to this reference. */
flush_range_ref (&orng, rr, cc);
/* This asks -- does the referenced region
* intersect the region being moved at the:
*/
fn = ((BETWEEN (orng.hc, fm->lc, fm->hc) << 3) /* right? 8 */
| (BETWEEN (orng.lc, fm->lc, fm->hc) << 2) /* left? 4 */
| (BETWEEN (orng.hr, fm->lr, fm->hr) << 1) /* bottom? 2 */
| BETWEEN (orng.lr, fm->lr, fm->hr)); /* top? 1 */
/* In this switch, a union of masks represents a conjunction
* of intersections. So, LEFT | TOP means `interects at left
* and top'.
*/
switch (fn)
{
/* Most of the time, the referenced region is moved only
* if the reference is relative.
*/
case LEFT | TOP:
case LEFT | BOTTOM:
case RIGHT | TOP:
case RIGHT | BOTTOM:
/* There used to be a warning given to the user here, but
* that seems silly, no?
*/
case 0:
case TOP:
case BOTTOM:
case TOP | BOTTOM:
case LEFT:
case RIGHT:
case LEFT | RIGHT:
if ((*fp) & LRREL)
orng.lr += dn;
if ((*fp) & HRREL)
orng.hr += dn;
if ((*fp) & LCREL)
orng.lc += ov;
if ((*fp) & HCREL)
orng.hc += ov;
break;
/* If the referenced range contains rows or columns that
* are entirely within the region being moved, then
* the region is moved, shrunk or stretched.
*/
case LEFT | BOTTOM | TOP:
case RIGHT | BOTTOM | TOP:
case RIGHT | LEFT | TOP:
case RIGHT | LEFT | BOTTOM:
case RIGHT | LEFT | BOTTOM | TOP:
if (fn != (LEFT | BOTTOM | TOP))
orng.hc += ov;
if (fn != (RIGHT | BOTTOM | TOP))
orng.lc += ov;
if (fn != (RIGHT | LEFT | TOP))
orng.hr += dn;
if (fn != (RIGHT | LEFT | BOTTOM))
orng.lr += dn;
break;
}
PUT_RNG (fp + 1, &orng); /* Patch the bytecode. */
push_stack (moving, (VOIDSTAR) fp);
push_stack (moving, (VOIDSTAR) TO_MAGIC (rr + dn, cc + ov));
push_stack (moving, DEF_RNG);
break;
default:
{
struct function *fun;
if (*fp < USR1)
fun = &the_funs[*fp];
else
fun = &usr_funs[*fp - USR1][fp[1]];
if (fun->fn_comptype & C_T)
{
flush_ref_fm (&timer_cells, rr, cc);
add_ref_fm (&timer_cells, rr + dn, cc + ov);
}
}
break;
}
}
}
/* Next, adjust references TO the region */
for (n = 0; cp->cell_refs_from && n < cp->cell_refs_from->refs_used; n++)
{
/* The second enclosed loop over the bytecode will fix all of the
* references to this cell. This loop is here because a
* refs_fm structure may contain more than one occurence of the
* referencing cell. We don't want to adjust the same bytecode
* twice.
*/
while ((n < cp->cell_refs_from->refs_used - 1)
&& (cp->cell_refs_from->fm_refs[n].ref_row ==
cp->cell_refs_from->fm_refs[n + 1].ref_row)
&& (cp->cell_refs_from->fm_refs[n].ref_col ==
cp->cell_refs_from->fm_refs[n + 1].ref_col))
++n;
/* For each cell that referenced this one, look
* at the type of reference involved
*/
frr = cp->cell_refs_from->fm_refs[n].ref_row;
fcc = cp->cell_refs_from->fm_refs[n].ref_col;
/* Unless the reference is from inside the region we're moving, in
* which case, it has already been adjusted.
*
* (This test seems unnecessary but harmless. -tl)
*/
if (BETWEEN (frr, fm->lr, fm->hr) && BETWEEN (fcc, fm->lc, fm->hc))
continue;
/* Find the cell that references cp. */
fcp = find_cell (frr, fcc);
/* Search the byte-code for the reference. */
for (fn = 0; fcp->cell_refs_to && fn < fcp->cell_refs_to->refs_used; fn++)
{
ffp = &(fcp->cell_formula[fcp->cell_refs_to->to_refs[fn]]);
switch (*ffp)
{
case R_CELL:
case R_CELL | ROWREL:
case R_CELL | COLREL:
case R_CELL | ROWREL | COLREL:
trr = GET_ROW (ffp + 1);
tcc = GET_COL (ffp + 1);
if (trr != rr || tcc != cc)
continue;
{
CELLREF old_tr = trr;
CELLREF old_tc = tcc;
/* Find the cell that fcp should reference now. */
if (!((*ffp) & ROWREL))
{
trr += dn;
PUT_ROW (ffp + 1, trr);
}
if (!((*ffp) & COLREL))
{
tcc += ov;
PUT_COL (ffp + 1, tcc);
}
else
/* If this is an abs reference, it doesn't change. */
continue;
/* Get rid of the now-invalid backpointer */
{
CELL * old_reffed = find_cell (old_tr, old_tc);
flush_ref_fm (&(old_reffed->cell_refs_from), frr, fcc);
}
}
if (BETWEEN (trr, fm->lr, fm->hr) && BETWEEN (tcc, fm->lc, fm->hc))
{
push_stack (moving, (VOIDSTAR) TO_MAGIC (frr, fcc));
push_stack (moving, (VOIDSTAR) TO_MAGIC (trr, tcc));
push_stack (moving, DEF_REF);
}
else
{
cp = find_or_make_cell (trr, tcc);
add_ref_fm (&(cp->cell_refs_from), frr, fcc);
}
case VAR:
/* This case is taken care of by {start,finish}_shift_vars */
continue;
case RANGE:
case RANGE | LRREL:
case RANGE | LRREL | LCREL:
case RANGE | LRREL | LCREL | HCREL:
case RANGE | LRREL | HCREL:
case RANGE | LRREL | HRREL:
case RANGE | LRREL | HRREL | LCREL:
case RANGE | LRREL | HRREL | LCREL | HCREL:
case RANGE | LRREL | HRREL | HCREL:
case RANGE | HRREL:
case RANGE | HRREL | LCREL:
case RANGE | HRREL | LCREL | HCREL:
case RANGE | HRREL | HCREL:
case RANGE | LCREL:
case RANGE | LCREL | HCREL:
case RANGE | HCREL:
GET_RNG (ffp + 1, &orng);
if (!BETWEEN (rr, orng.lr, orng.hr) || !BETWEEN (cc, orng.lc, orng.hc))
break;
val = ((BETWEEN (orng.hc, fm->lc, fm->hc) << 3) /* right */
+ (BETWEEN (orng.lc, fm->lc, fm->hc) << 2) /* left */
+ (BETWEEN (orng.hr, fm->lr, fm->hr) << 1) /* bottom */
+ BETWEEN (orng.lr, fm->lr, fm->hr)); /* top */
/* If the reference is absolute, or relative only in directions
* that aren't changing, there's nothing to do.
*/
if (!(*ffp == RANGE
|| (!dn && ((*ffp) | LRREL | HRREL) == (RANGE | LRREL | HRREL))
|| (!ov && ((*ffp) | LCREL | HCREL) == (RANGE | LCREL | HCREL))))
continue;
/* If it's a case we don't know how to adjust, there's
* nothing to do. If there is no overlap, there's nothing to
* do.
*/
if ((val != (LEFT | BOTTOM | TOP))
&& (val != (RIGHT | BOTTOM | TOP))
&& (val != (RIGHT | LEFT | TOP))
&& (val != (RIGHT | LEFT | BOTTOM))
&& (val != (RIGHT | LEFT | BOTTOM | TOP)))
continue;
flush_range_ref (&orng, frr, fcc);
if (val != (RIGHT | LEFT | BOTTOM))
orng.lr += dn;
if (val != (RIGHT | LEFT | TOP))
orng.hr += dn;
if (val != (RIGHT | BOTTOM | TOP))
orng.lc += ov;
if (val != (LEFT | BOTTOM | TOP))
orng.hc += ov;
PUT_RNG (ffp + 1, &orng);
push_stack (moving, (VOIDSTAR) ffp);
push_stack (moving, (VOIDSTAR) TO_MAGIC (frr, fcc));
push_stack (moving, DEF_RNG);
continue;
#ifdef TEST
default:
{ struct function *fun; if (*ffp < USR1) fun =
&the_funs[*ffp]; else if (*ffp >= SKIP) fun = 0, panic
("SKIP? in shift_outside()"); else fun =
&usr_funs[*ffp][ffp[1]];
if ((fun->fn_comptype & C_T) == 0) io_error_msg
("Unknown byte (%d) for reference_to #%d %d",
*ffp, fn, fcp->cell_refs_to->to_refs[fn]);
}
break;
#endif
}
}
}
}
while (ptr = pop_stack (moving))
{
if (ptr == DEF_REF)
{
val = (unsigned long) pop_stack (moving);
trr = MAGIC_ROW (val);
tcc = MAGIC_COL (val);
val = (unsigned long) pop_stack (moving);
cp = find_or_make_cell (trr, tcc);
add_ref_fm (&(cp->cell_refs_from), MAGIC_ROW (val), MAGIC_COL (val));
}
else if (ptr == DEF_RNG)
{
val = (unsigned long) pop_stack (moving);
cur_row = MAGIC_ROW (val);
cur_col = MAGIC_COL (val);
ffp = (unsigned char *) pop_stack (moving);
GET_RNG (ffp + 1, &orng);
add_range_ref (&orng);
if (my_cell)
panic ("shift_outside: my_cell lost.");
/* If this panic occurs, the caller should be recomputing
* my_cell after shift_outside returns (and this useful panic
* will have to be removed or my_cell set temporarily to 0).
*/
}
#ifdef TEST
else
panic ("Now what (%p)?", ptr);
#endif
}
for_all_vars (finish_shift_var);
/* flush_stack(moving); */
}
/* The formula in cell my_cell has moved by DN down and OV over, adjust
everything so it'll still work */
#ifdef __STDC__
void
shift_formula (int r, int c, int dn, int ov)
#else
void
shift_formula (r, c, dn, ov)
int r; /* Address of my_cell. */
int c;
int dn;
int ov;
#endif
{
int n;
unsigned char *fp;
for (n = 0; n < my_cell->cell_refs_to->refs_used; n++)
{
fp = &(my_cell->cell_formula[my_cell->cell_refs_to->to_refs[n]]);
switch (*fp)
{
case F_ROW:
case F_COL:
push_cell (cur_row, cur_col);
break;
case R_CELL:
case R_CELL | ROWREL:
case R_CELL | COLREL:
case R_CELL | ROWREL | COLREL:
{
CELLREF trr, tcc;
CELL *tcp;
/* These are more difficult */
trr = GET_ROW (fp + 1);
tcc = GET_COL (fp + 1);
tcp = find_cell (trr, tcc);
#ifdef TEST
if (!tcp)
panic ("Can't find_cell(%s) in shift_formula", cell_name (trr, tcc));
#endif
flush_ref_fm (&(tcp->cell_refs_from), cur_row - dn, cur_col - ov);
if (((*fp) & ROWREL) && dn)
{
trr += dn;
PUT_ROW (fp + 1, trr);
}
if (((*fp) & COLREL) && ov)
{
tcc += ov;
PUT_COL (fp + 1, tcc);
}
tcp = find_or_make_cell (trr, tcc);
add_ref_fm (&(tcp->cell_refs_from), cur_row, cur_col);
}
break;
case RANGE:
case RANGE | LRREL:
case RANGE | LRREL | LCREL:
case RANGE | LRREL | LCREL | HCREL:
case RANGE | LRREL | HCREL:
case RANGE | LRREL | HRREL:
case RANGE | LRREL | HRREL | LCREL:
case RANGE | LRREL | HRREL | LCREL | HCREL:
case RANGE | LRREL | HRREL | HCREL:
case RANGE | HRREL:
case RANGE | HRREL | LCREL:
case RANGE | HRREL | LCREL | HCREL:
case RANGE | HRREL | HCREL:
case RANGE | LCREL:
case RANGE | LCREL | HCREL:
case RANGE | HCREL:
{
struct rng orng;
GET_RNG (fp + 1, &orng);
flush_range_ref (&orng, cur_row - dn, cur_col - ov);
if ((*fp) & LRREL)
orng.lr += dn;
if ((*fp) & HRREL)
orng.hr += dn;
if ((*fp) & LCREL)
orng.lc += ov;
if ((*fp) & HCREL)
orng.hc += ov;
PUT_RNG (fp + 1, &orng);
add_range_ref (&orng);
/* sparse array bug fixed here */
my_cell = find_cell (r, c);
}
break;
case VAR:
{
struct var *v;
struct cell *tcp;
bcopy (&fp[1], &v, sizeof (struct var *));
flush_ref_fm (&(v->var_ref_fm), cur_row - dn, cur_col - ov);
add_ref_fm (&(v->var_ref_fm), cur_row, cur_col);
switch (v->var_flags)
{
case VAR_UNDEF:
break;
case VAR_CELL:
tcp = find_cell (v->v_rng.lr, v->v_rng.lc);
#ifdef TEST
if (!tcp)
panic ("Can't find_cell(%s) in shift_formula", cell_name (v->v_rng.lr, v->v_rng.lc));
#endif
flush_ref_fm (&(tcp->cell_refs_from), cur_row - dn, cur_col - ov);
add_ref_fm (&(tcp->cell_refs_from), cur_row, cur_col);
break;
case VAR_RANGE:
flush_range_ref (&(v->v_rng), cur_row - dn, cur_col - ov);
add_range_ref (&(v->v_rng));
/* sparse array bug fixed here */
my_cell = find_cell (r, c);
break;
#ifdef TEST
default:
panic ("Unknown var type %d", v->var_flags);
#endif
}
}
break;
default:
{
struct function *fun;
if (*fp < USR1)
fun = &the_funs[*fp];
#ifdef TEST
else if (*fp >= SKIP)
fun = 0, panic ("SKIP? in shift_formula?");
#endif
else
fun = &usr_funs[*fp][fp[1]];
/* These are easy */
if (fun->fn_comptype & C_T)
{
flush_ref_fm (&timer_cells, cur_row - dn, cur_col - ov);
add_ref_fm (&timer_cells, cur_row, cur_col);
}
#ifdef TEST
else
panic ("How do I deal with byte %d in shift_formula()?", *fp);
#endif
}
break;
}
}
}
/* ---------------- Routines for dealing with async functions -------------- */
/* This function is called when the alarm has gone off (but not from inside
* the signal handler!). It schedules timer_cells->fm_refs for recalc.
*/
#ifdef __STDC__
void
cell_alarm (void)
#else
void
cell_alarm ()
#endif
{
int n;
static time_t last_time = 0;
if (timer_active)
{
time_t this_time = time(0);
if ((this_time - last_time) < cell_timer_seconds)
return;
last_time = this_time;
current_cycle++;
for (n = 0; n < timer_cells->refs_used; n++)
push_cell (timer_cells->fm_refs[n].ref_row,
timer_cells->fm_refs[n].ref_col);
}
}
/* All the timer_cells are going away, 'cuz everything is going away. . . */
#ifdef __STDC__
void
flush_all_timers (void)
#else
void
flush_all_timers ()
#endif
{
if (timer_active)
{
#ifdef SPLIT_REFS
timer_cells->refs_used = 0;
#else
flush_fm_ref (timer_cells);
timer_cells = 0;
#endif
timer_active = 0;
}
}
/* Add CUR_ROW, CUR_COL to the list of active timer-cells, turning on
the timer_active, if it isn't already */
#ifdef __STDC__
void
add_timer_ref (int whereto)
#else
void
add_timer_ref (whereto)
int whereto;
#endif
{
add_ref_to (whereto);
add_ref_fm (&timer_cells, cur_row, cur_col);
++timer_active;
}
/* ---------- Routines and vars for dealing with the eval FIFO ------------ */
static struct cell_buf cell_buffer;
/* Start up the FIFO of cells to update */
#ifdef __STDC__
void
init_refs (void)
#else
void
init_refs ()
#endif
{
cell_buffer.size = FIFO_START;
cell_buffer.buf = (struct pos *) ck_malloc (cell_buffer.size * sizeof (struct pos));
bzero (cell_buffer.buf, cell_buffer.size * sizeof (struct pos));
cell_buffer.push_to_here = cell_buffer.buf;
cell_buffer.pop_frm_here = cell_buffer.buf;
the_vars = hash_new ();
}
/* Push the cells in REF onto the FIFO. This calls push_cell to do the
actual work. . . */
#ifdef __STDC__
void
push_refs (struct ref_fm *ref)
#else
void
push_refs (ref)
struct ref_fm *ref;
#endif
{
int n;
if (!ref || !ref->refs_used)
return;
n = ref->refs_used;
while (n--)
{
#ifdef TEST
CELL *cp;
if (debug & 04)
io_error_msg ("Push %s", cell_name (ref->fm_refs[n].ref_row, ref->fm_refs[n].ref_col));
cp = find_cell (ref->fm_refs[n].ref_row, ref->fm_refs[n].ref_col);
if (cp->cell_cycle == current_cycle)
{
if (debug & 01)
io_error_msg ("Cycle detected from %s to %s",
cell_name (cur_row, cur_col),
cell_name (ref->fm_refs[n].ref_row, ref->fm_refs[n].ref_col));
push_cell (ref->fm_refs[n].ref_row,
ref->fm_refs[n].ref_col);
}
else
#endif
push_cell (ref->fm_refs[n].ref_row, ref->fm_refs[n].ref_col);
}
}
/* Push a cell onto the FIFO of cells to evaluate, checking for cells
that are already on the FIFO, etc.
This does not implement best-order recalculation, since there may be
intersecting branches in the dependency tree, however, it's close enough
for most people.
*/
#ifdef __STDC__
void
push_cell (CELLREF row, CELLREF col)
#else
void
push_cell (row, col)
CELLREF row;
CELLREF col;
#endif
{
struct pos *dup;
CELL *cp;
struct ref_fm *rf;
if (cell_buffer.push_to_here + 1 == cell_buffer.pop_frm_here || (cell_buffer.pop_frm_here == cell_buffer.buf && cell_buffer.push_to_here == cell_buffer.buf + (cell_buffer.size - 1)))
{
int f, t, from_num;
f = cell_buffer.pop_frm_here - cell_buffer.buf;
t = cell_buffer.push_to_here - cell_buffer.buf;
from_num = cell_buffer.size - f;
cell_buffer.size FIFO_INC;
cell_buffer.buf = (struct pos *) ck_realloc ((VOIDSTAR) cell_buffer.buf, cell_buffer.size * sizeof (struct pos));
if (t == 0)
{
cell_buffer.push_to_here = cell_buffer.buf + f + from_num;
cell_buffer.pop_frm_here = cell_buffer.buf + f;
}
else if (t > f)
{
cell_buffer.push_to_here = cell_buffer.buf + t;
cell_buffer.pop_frm_here = cell_buffer.buf + f;
}
else
{
cell_buffer.push_to_here = cell_buffer.buf + t;
cell_buffer.pop_frm_here = cell_buffer.buf + (cell_buffer.size - from_num);
if (from_num)
bcopy (cell_buffer.buf + f,
cell_buffer.pop_frm_here,
from_num * sizeof (struct pos));
}
}
#if 1
if (cell_buffer.pop_frm_here != cell_buffer.push_to_here)
{
dup = cell_buffer.pop_frm_here;
cp = find_cell (row, col);
if (!cp)
{
return;
}
rf = cp->cell_refs_from;
for (; dup != cell_buffer.push_to_here;)
{
if (dup->row == row && dup->col == col)
{
#ifdef TEST
if (debug & 010)
io_error_msg ("Flushed dup ref to %s", cell_name (row, col));
#endif
*dup = *(cell_buffer.pop_frm_here);
cell_buffer.pop_frm_here++;
if (cell_buffer.pop_frm_here == cell_buffer.buf + cell_buffer.size)
cell_buffer.pop_frm_here = cell_buffer.buf;
break;
}
#if 0
if (rf)
{
for (n = 0; n < rf->refs_used; n++)
if (rf->fm_refs[n].ref_row == dup->row && rf->fm_refs[n].ref_col == dup->col)
{
#ifdef TEST
if (debug & 01)
io_error_msg ("Swapped %s and %s", cell_name (row, col), cell_name (dup->row, dup->col));
#endif
dup->row = row;
dup->col = col;
row = rf->fm_refs[n].ref_row;
col - rf->fm_refs[n].ref_col;
goto breakout;
}
}
#endif
if (++dup == cell_buffer.buf + cell_buffer.size)
dup = cell_buffer.buf;
}
}
#endif
cell_buffer.push_to_here->row = row;
cell_buffer.push_to_here->col = col;
cell_buffer.push_to_here++;
if (cell_buffer.push_to_here == cell_buffer.buf + cell_buffer.size)
cell_buffer.push_to_here = cell_buffer.buf;
}
/* Pop a cell off CELL_BUFFER, and evaluate it, displaying the result. . .
This returns 0 if there are no more cells to update, or if it gets
an error. */
#ifdef __STDC__
int
eval_next_cell (void)
#else
int
eval_next_cell ()
#endif
{
CELL *cp;
static loop_counter = 40;
if (cell_buffer.pop_frm_here == cell_buffer.push_to_here)
return 0;
cur_row = cell_buffer.pop_frm_here->row;
cur_col = cell_buffer.pop_frm_here->col;
cell_buffer.pop_frm_here++;
if (cell_buffer.pop_frm_here == cell_buffer.buf + cell_buffer.size)
cell_buffer.pop_frm_here = cell_buffer.buf;
cp = find_cell (cur_row, cur_col);
if (cp)
{
if (cp->cell_cycle == current_cycle)
--loop_counter;
else
loop_counter = 40;
update_cell (cp);
io_pr_cell (cur_row, cur_col, cp);
return loop_counter;
}
else
return 0;
}
#ifdef TEST
void
cell_buffer_contents ()
{
struct pos *ptr;
if (cell_buffer.pop_frm_here != cell_buffer.push_to_here)
{
ptr = cell_buffer.pop_frm_here;
for (;;)
{
printf ("Ref to %s\r\n", cell_name (ptr->row, ptr->col));
if (++ptr == cell_buffer.buf + cell_buffer.size)
ptr = cell_buffer.buf;
if (ptr == cell_buffer.push_to_here)
break;
}
}
printf ("End of buffer\r\n");
}
#endif
/* ----------------- Routines for dealing with variables ------------------ */
/* This sets the variable V_NAME to V_NEWVAL
It returns error msg, or 0 on success.
All the appropriate cells have their ref_fm arrays adjusted appropriatly
This could be smarter; when changing a range var, only the cells that
were in the old value but not in the new one need their references flushed,
and only the cells that are new need references added.
This might also be changed to use add_range_ref()?
*/
#ifdef __STDC__
char *
new_var_value (char *v_name, int v_namelen, char *v_newval)
#else
char *
new_var_value (v_name, v_namelen, v_newval)
char *v_name;
int v_namelen;
char *v_newval;
#endif
{
struct var *var;
int n;
int newflag;
struct rng tmp_rng;
cur_row = MIN_ROW;
cur_col = MIN_COL;
if (v_newval && *v_newval)
{
n = parse_cell_or_range (&v_newval, &tmp_rng);
if (!n)
return "Can't parse cell or range";
if (*v_newval)
return "Junk after cell or range";
newflag = ((n | ROWREL | COLREL) == (R_CELL | ROWREL | COLREL)) ? VAR_CELL : VAR_RANGE;
}
else
{
tmp_rng.lr = tmp_rng.hr = NON_ROW;
tmp_rng.lc = tmp_rng.hc = NON_COL;
newflag = VAR_UNDEF;
}
var = find_or_make_var (v_name, v_namelen);
if (var->var_ref_fm)
{
if (var->var_flags != VAR_UNDEF)
{
for (n = 0; n < var->var_ref_fm->refs_used; n++)
{
flush_range_ref (&(var->v_rng),
var->var_ref_fm->fm_refs[n].ref_row,
var->var_ref_fm->fm_refs[n].ref_col);
}
}
var->v_rng = tmp_rng;
if (var->v_rng.lr != NON_ROW)
{
for (n = 0; n < var->var_ref_fm->refs_used; n++)
{
cur_row = var->var_ref_fm->fm_refs[n].ref_row;
cur_col = var->var_ref_fm->fm_refs[n].ref_col;
add_range_ref (&(var->v_rng));
}
}
for (n = 0; n < var->var_ref_fm->refs_used; n++)
push_cell (var->var_ref_fm->fm_refs[n].ref_row,
var->var_ref_fm->fm_refs[n].ref_col);
}
else
var->v_rng = tmp_rng;
var->var_flags = newflag;
return 0;
}
#ifdef __STDC__
void
for_all_vars (void (*func) (char *, struct var *))
#else
void
for_all_vars (func)
void (*func) ();
#endif
{
hash_apply (the_vars, func);
}
/* Find a variable in the list of variables, or create it if it doesn't
exist. Takes a name and a length so the name doesn't have to be
null-terminated
*/
#ifdef __STDC__
struct var *
find_or_make_var (char *string, int len)
#else
struct var *
find_or_make_var (string, len)
char *string;
int len;
#endif
{
struct var *ret;
int ch;
ch = string[len];
string[len] = '\0';
ret = (struct var *) hash_find (the_vars, string);
if (ret)
{
string[len] = ch;
return ret;
}
ret = (struct var *) ck_malloc (sizeof (struct var) + len);
bcopy (string, ret->var_name, len + 1);
ret->var_flags = VAR_UNDEF;
ret->v_rng.lr = 0;
ret->v_rng.lc = 0;
ret->v_rng.hr = 0;
ret->v_rng.hc = 0;
ret->var_ref_fm = 0;
hash_insert (the_vars, ret->var_name, ret);
string[len] = ch;
return ret;
}
/* Like find-or-make-var except returns 0 if it doesn't exist */
#ifdef __STDC__
struct var *
find_var (char *string, int len)
#else
struct var *
find_var (string, len)
char *string;
int len;
#endif
{
int ch;
struct var *ret;
ch = string[len];
string[len] = '\0';
ret = (struct var *) hash_find (the_vars, string);
string[len] = ch;
return ret;
}
/* This adds a reference from CUR_ROW,CUR_COL to the variable VAR
It calls add_ref or add_range_ref to have the cell(s) in VAR be
referenced by CUR_ROW,CUR_COL
*/
#ifdef __STDC__
void
add_var_ref (void * vvar)
#else
void
add_var_ref (vvar)
void * vvar;
#endif
{
struct var *var = (struct var *)vvar;
add_ref_fm (&(var->var_ref_fm), cur_row, cur_col);
switch (var->var_flags)
{
case VAR_UNDEF:
break;
case VAR_CELL:
add_ref (var->v_rng.lr, var->v_rng.lc);
break;
case VAR_RANGE:
add_range_ref (&(var->v_rng));
break;
#ifdef TEST
default:
panic ("Unknown var type %d in add_var_ref", var->var_flags);
#endif
}
}
#ifdef __STDC__
static void
flush_var (char *name, struct var *var)
#else
static void
flush_var (name, var)
char *name;
struct var *var;
#endif
{
#ifdef SPLIT_REFS
if (var->var_ref_fm)
free (var->var_ref_fm);
#endif
free (var);
}
/* Free up all the variables, and (if SPLIT_REFS) the ref_fm structure
associated with each variable. Note that this does not get rid of
the struct var *s in cell expressions, so it can only be used when all
the cells are being freed also
*/
#ifdef __STDC__
void
flush_variables (void)
#else
void
flush_variables ()
#endif
{
for_all_vars (flush_var);
hash_die (the_vars);
the_vars = hash_new ();
}